package modifyx

import (
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/convertx"
	"gitee.com/zhongguo168a/gocodes/datax/mapx"
	"sync"
)

type M = map[string]interface{}
type A = []interface{}

// className = origin["_type"]
func NewSource() *Source {
	return NewSourceWith(datax.M{})
}

func NewSourceWith(data datax.M) *Source {
	return &Source{
		origin:     mapx.Flat(data, true),
		typ:        mapx.String(data, "_type"),
		modifyList: map[string][]IModify{},
	}
}

// 数据源
// 通常加载后，数据源不可变，作为配置文件的映射
// 采用数据源的目的是为了避免数据的克隆影响性能，毕竟需要修正的地方有限
type Source struct {
	// 数据
	origin map[string]interface{}
	//
	modifyList map[string][]IModify
	//
	id string
	//
	catalog string
	// 作为结构时的类名
	typ string
	//
	mutex sync.RWMutex
}

func (s *Source) ClassId() string {
	return s.id
}
func (s *Source) SetClassId(val string) {
	s.id = val
}
func (s *Source) ClassKey() string {
	return s.catalog + "/" + s.id
}

func (s *Source) ClassCatalog() string {
	return s.catalog
}
func (s *Source) ClassType() string {
	return s.typ
}

// co.CModify
func (p *Source) AddModify(val IModify) {
	list, ok := p.modifyList[val.Path()]
	if ok {
		list = append(list, val)
		p.modifyList[val.Path()] = list
	} else {
		p.modifyList[val.Path()] = []IModify{
			val,
		}
	}

}

func (s *Source) GetOrigin(path string) interface{} {
	s.mutex.RLock()
	lastValue := s.origin[path]
	s.mutex.RUnlock()
	return lastValue
}
func (s *Source) IsNil(path string) bool {
	s.mutex.RLock()
	lastValue := s.origin[path]
	s.mutex.RUnlock()
	return lastValue == nil
}
func (s *Source) MapKeys(path string) []string {
	s.mutex.RLock()
	lastValue := s.origin[path+"/keys"]
	s.mutex.RUnlock()
	return lastValue.([]string)
}

// 获取 map或者array的长度，需要先判断 IsNil
func (s *Source) Length(path string) int {
	s.mutex.RLock()
	lastValue := s.origin[path]
	s.mutex.RUnlock()
	return convertx.AnyToInt(lastValue)
}

func (s *Source) Get(ctx interface{}, path string) interface{} {
	s.mutex.RLock()
	lastValue := s.origin[path]
	modifys := s.modifyList[path]
	s.mutex.RUnlock()
	if modifys == nil {
		return lastValue
	}

	for _, imodify := range modifys {
		modify := imodify.(IModify)
		lastValue = modify.Value(ctx, lastValue)
	}

	return lastValue
}

func (s *Source) Dispose() {
	s.origin = nil
}

func (s *Source) Clone() (r *Source) {
	r = NewSourceWith(mapx.CloneMap(s.origin))
	return
}
